HDU 5735 Born Slippy(dp、分块、可持久化)

题意:

$N\le 2^{16}个节点的一棵树,点权w_i < 2^{16},现从树上抓出来一条链序列$
$对于起点s\in [1, N],找出1个序列,v_1=s, v_2, \cdots, v_m$
$使得f(s)=w_{v_1}+\sum\limits_{i=2}^{m}w_{v_i} \text{ opt } w_{v_{i-1}}最大,opt可以是AND,OR,XOR$
$求每个f(i)$

分析:

$考虑序列上的情况,以and为例,显然有dp,f[i]=ans[i]-w_s=\max\limits_{j < i} \{ f[j]+w_j\text{ and }w_i \}$
$蓝儿转移O(n),总复杂度O(n^2)是不行的,考虑维护东西降低转移复杂度$
$观察w_i<2^{16},我们把转移拆一下$
$f[i]=\max\limits_{j < i} \{ f[j]+w_j[后8位]\text{ and }w_i[后8位]+w_j[前8位]\text{ and }w_i[前8位] \text{<<} 8 \}$
$之后我们维护1个,ds[x][y]:=表示w_j前8位为x,w_i后8位为y时, f[j]+w_j[后8位]\text{ and }w_i[后8位]的最值$
$令w_i=a\text{ << }8\text{ | }b,更新f[i]只要枚举x,f[i]=\max\{ ds[x][b]+x\text{ and }a\text{ << }8 \}$
$转移复杂度就变成了O(\sqrt{n}),同理,用f[i]更新ds[x][y]$
$推广到树上,从根往下转移的时候,备份被修改的,之后再拷贝回去(即可持久化一下)$
$总时间复杂度为O(n\sqrt{n})$

代码:

//
//  Created by TaoSama on 2016-07-25
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = (1 << 16) + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const int S = 1 << 8;

typedef unsigned int UINT;
int n, w[N];
vector<int> G[N];
UINT f[N], backup[N][S], ds[S][S]; //w_fa prefix8, w_u suffix8 -> max suffix

char op[10];
UINT opt(UINT a, UINT b) {
    if(*op == 'A') return a & b;
    else if(*op == 'O') return a | b;
    return a ^ b;
}

void getMax(UINT& x, UINT y) {
    if(x == -1 || x < y) x = y;
}

void dfs(int u) {
    UINT a = w[u] >> 8, b = w[u] & 255;

    UINT tmp = 0;
    for(int i = 0; i < S; ++i)
        if(ds[i][b] != -1)
            getMax(tmp, ds[i][b] + (opt(i, a) << 8));

    f[u] = w[u] + tmp;
    memcpy(backup[u], ds[a], S << 2);
    for(int i = 0; i < S; ++i)
        getMax(ds[a][i], tmp + opt(i, b));

    for(int v : G[u]) dfs(v);
    memcpy(ds[a], backup[u], S << 2);
}

int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//  freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);
    clock_t _ = clock();

    int t; scanf("%d", &t);
    while(t--) {
        scanf("%d%s", &n, op);
        for(int i = 1; i <= n; ++i) scanf("%d", w + i);
        for(int i = 1; i <= n; ++i) G[i].clear();
        for(int i = 2; i <= n; ++i) {
            int fa; scanf("%d", &fa);
            G[fa].push_back(i);
        }

        memset(ds, -1, sizeof ds);
        dfs(1);

        int ans = 0;
        for(int i = 1; i <= n; ++i) {
            ans += 1LL * i * f[i] % MOD;
            if(ans >= MOD) ans -= MOD;
        }
        printf("%d\n", ans);
    }

#ifdef LOCAL
    printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
#endif
    return 0;
}